package com.nbstar.film.spider.engine;

import com.nbstar.film.spider.domain.FilmSpiderDomain;
import com.nbstar.film.spider.excel.IExcelReader;
import com.nbstar.film.spider.excel.IExcelWriter;
import com.nbstar.film.spider.pojo.DouBanFilmInfo;
import com.nbstar.film.spider.pojo.FilmInfo;
import com.nbstar.film.spider.spider.BaseSpider;
import com.nbstar.film.spider.spider.FilmSpider;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Data
public abstract class BaseSpiderEngine implements ISpiderEngine {

    protected IExcelReader excelReader = null;

    protected Map<String, BaseSpider> spiderMap = new HashMap<>();

    protected LinkedBlockingQueue<FilmSpiderDomain> filmSpiderQueue = new LinkedBlockingQueue<>();
    protected LinkedBlockingQueue<FilmSpiderDomain> retryList = new LinkedBlockingQueue<FilmSpiderDomain>();

    protected IExcelWriter excelWriter = null;

    protected int threadNum  = 5;

    protected String resultSheetName = "result";

    protected String resultExcelName = "result.xlsx";

    protected String readExcelPath = "";

    protected int readExcelSheetIndex = 0;

    protected int readExcelStartRow = 1;

    private ExecutorService spiderThreadPool;

    protected int totalSize = 0;
    protected AtomicInteger counter = new AtomicInteger(0);

    private int retry = 0;
    private AtomicInteger retryCounter = new AtomicInteger(0);
    private int retrySize = 0;

    @Override
    public LinkedBlockingQueue<FilmSpiderDomain> loadExcel(String modelPath, int sheetIndex, int startRow) throws IOException {
        excelReader.setFilmSpiderQueue(filmSpiderQueue);
        return excelReader.loadExcel(modelPath, sheetIndex, startRow);
    }

    public  void executeDomain(FilmSpiderDomain domain) throws Exception  {
        spiderMap.forEach((k, v)->{
            FilmInfo filmInfo;
            if (k.equals(FilmSpiderDomain.SPIDER_FROM_DOUBAN)) {
                filmInfo = v.runProxy(domain.getRaw().getFilmName());
                domain.getRaw().setImdbUrl(((DouBanFilmInfo)filmInfo).getImdb());
            } else if (k.equals(FilmSpiderDomain.SPIDER_FROM_IMDB)) {
                filmInfo = v.run(domain.getRaw().getImdbUrl());
                filmInfo.setCname(domain.getRaw().getFilmName());
            } else {
                filmInfo = v.run(domain.getRaw().getFilmName());
            }

            log.debug(filmInfo.toString());
            domain.put(k, filmInfo);

        });

    }
    public abstract boolean checkDomainSucc(FilmSpiderDomain domain);


    public void registSpider(String spiderType, BaseSpider spider) {
        spiderMap.put(spiderType, spider);
    }

    /**
     * 保存爬取到的信息，默认保存到excel里面,如果需要其他保存方式重写该方法
     * @param domain
     */
    protected   void saveDomain(FilmSpiderDomain domain) {
        this.excelWriter.writeVedio2Excel(domain);
    }

    @Override
    public void execute(int threadNum) throws IOException {
        spiderThreadPool = Executors.newFixedThreadPool(threadNum);
        distributionTask(filmSpiderQueue, spiderThreadPool);
    }

    private void distributionTask(LinkedBlockingQueue<FilmSpiderDomain> task, ExecutorService threadPool) {
        for ( FilmSpiderDomain filmSpiderDomain : task) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        executeDomain(filmSpiderDomain);
                    } catch (Exception e) {
                        log.error(e.getMessage());
                    } finally {
                        log.debug("Save result info :{}", filmSpiderDomain);
                        if (getRetry() <= 0 || checkDomainSucc(filmSpiderDomain)) {
                            saveDomain(filmSpiderDomain);
                        } else {
                            log.info("file {} is fail , not save!", filmSpiderDomain.getRaw().getFilmName());
                        }

                        if (!checkDomainSucc(filmSpiderDomain)) {
                            retryList.add(filmSpiderDomain);
                        }
                    }

                    counter.getAndIncrement();
                }
            });
        }
        log.info("任务分配完毕！");
    }

    public void start() throws IOException {
        Date start = new Date();
        Runnable monitor = new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    int finshed = counter.get();
                    Date now = new Date();
                    long diffmilis = (now.getTime() - start.getTime()) /1000;

                    long h = (diffmilis / (60*60));
                    long m = (diffmilis % 3600) / 60;
                    long s = (diffmilis % 3600) % 60;


                    log.info("total size:{}, retry size:{} finished:{}|{}%, cost:{}小时:{}分:{}秒, ", totalSize, retrySize, finshed, 100 * ((double)finshed / totalSize), h, m, s);
                    //log.info("retry size:{} finished:{}|{}%, cost:{}小时:{}分:{}秒, ", totalSize, finshed, 100 * ((double)finshed / totalSize), h, m, s);
                    if (retry != 0 && totalSize != 0 && finshed == totalSize) {
                        log.info("开始重新爬取失败数据....");
                        filmSpiderQueue.clear();

                        filmSpiderQueue.addAll(retryList);
                        totalSize += retryList.size();
                        retrySize += retryList.size();
                        //counter.set(0);
                        retryList.clear();
                        distributionTask(filmSpiderQueue, spiderThreadPool);
                        retry--;

                    }

                    if (retry == 0 && totalSize != 0 && finshed == totalSize) {
                        log.info("数据处理完成！");
                        break;
                    }
                }
            }
        };
        initEngine();
        loadExcel(readExcelPath, readExcelSheetIndex, readExcelStartRow);
        excelWriter.initEXcelFile();
        totalSize = filmSpiderQueue.size();
        new Thread(monitor).start();
        execute(threadNum);
    }


}
